package system

import (
	consts2 "MoSkeleton/config/consts"
	"MoSkeleton/framework"
	"MoSkeleton/framework/core/dto/request"
	"MoSkeleton/framework/core/dto/response"
	"MoSkeleton/framework/core/models"
	"MoSkeleton/framework/utils"
	"MoSkeleton/models/system"
	"MoSkeleton/services/base"
	s1 "MoSkeleton/web/pogos/request/system"
	system2 "MoSkeleton/web/pogos/response/system"
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"gorm.io/gorm"
	"strconv"
)

type SysDictTypeService struct {
	base.BaseService
}

/*
select id, dict_name, dict_type, status, create_by, created_at, remark

	from sys_dict_type
*/
func (s *SysDictTypeService) GetDataTypePage(dictTypeReq *s1.SysDictTypePageReq) (*response.PageResult, error) {
	db := s.getDataTypeListVo(dictTypeReq)

	result := make([]system2.SysDictTypeItemResp, 0)
	var total int64
	err := db.Count(&total).Offset(dictTypeReq.Offset()).Limit(dictTypeReq.Limit()).Find(&result).Error
	if err != nil {
		return nil, err
	}
	pageResult := response.NewFromPageInfoAndData(&dictTypeReq.PageInfo, &result, total)
	return pageResult, nil
}

func (s *SysDictTypeService) ExportDataType(dictTypeReq *s1.SysDictTypePageReq) (*bytes.Buffer, error) {
	db := s.getDataTypeListVo(dictTypeReq)

	itemsResp := make([]system2.SysDictTypeItemResp, 0)
	err := db.Find(&itemsResp).Error
	if err != nil {
		return nil, err
	}

	titles := []string{"字典编号", "字典名称", "字典类型", "状态"}
	exportFile := utils.NewExportExcelFile()

	return exportFile.Export(consts2.Excel_Sheet_DictType, &titles, func(row, col int, colTitle string) (value any, ok bool, err error) {
		if len(itemsResp) <= row {
			return "", false, nil
		}
		itemResp := itemsResp[row]
		switch col {
		case 0:
			return "'" + strconv.FormatUint(itemResp.ID, 10), true, nil
		case 1:
			return itemResp.DictName, true, nil
		case 2:
			return itemResp.DictType, true, nil
		case 3:
			if *itemResp.Status == consts2.U_NORMAL {
				return "正常", true, nil
			} else {
				return "停用", true, nil
			}
		}
		return "", false, nil
	})
}

func (s *SysDictTypeService) GetDataTypeById(dictId string) (*system2.SysDictTypeItemResp, error) {
	id, err := strconv.ParseUint(dictId, 10, 64)
	if err != nil {
		return nil, err
	}
	itemResp, err := s.getCacheDataTypeById(id)
	if err != nil {
		return nil, err
	}
	if itemResp != nil {
		return itemResp, nil
	}
	itemResp, err = s.loadDataTypeToCacheById(id)
	return itemResp, err
}

func (s *SysDictTypeService) UpdateDateTypeById(updateReq *s1.SysDictTypeUpdateReq) (ok bool, err error) {
	dictType := system.SysDictType{}
	err = base.BaseDb.Transaction(func(tx *gorm.DB) error {
		oldDictType, err := dictType.SelectById(tx, updateReq.ID)
		if err != nil {
			ok = false
			return err
		}
		if oldDictType.DictType != updateReq.DictType {
			_, err := new(system.SysDictData).UpdateDictDataType(tx, oldDictType.DictType, updateReq.DictType)
			if err != nil {
				ok = false
				return err
			}
		}

		if err := utils.CopyStruct(&dictType, &updateReq); err != nil {
			ok = false
			return err
		}
		if _, err := dictType.UpdateDictType(tx); err != nil {
			ok = false
			return err
		}
		itemResp := new(system2.SysDictTypeItemResp)
		if err := utils.CopyStruct(itemResp, &dictType); err != nil {
			return err
		}
		if err := s.setDataTypeToCache(dictType.ID, itemResp); err != nil {
			return err
		}
		ok = true
		return nil
	})
	return
}

func (s *SysDictTypeService) AddDataType(addReq *s1.SysDictTypeAddReq) (bool, error) {
	dictType := system.SysDictType{}
	if err := utils.CopyStruct(&dictType, addReq); err != nil {
		return false, err
	}
	result := base.BaseDb.Save(&dictType)
	if result.Error == nil && result.RowsAffected > 0 {
		itemResp := new(system2.SysDictTypeItemResp)
		if err := utils.CopyStruct(itemResp, &dictType); err != nil {
			return false, err
		}
		if err := s.setDataTypeToCache(dictType.ID, itemResp); err != nil {
			return false, err
		}
	}
	return result.RowsAffected > 0, result.Error
}

func (s *SysDictTypeService) CheckDictTypeUnique(dictType string, idReq request.IDRequest) (bool, error) {
	idModel := models.IDModel{}
	result := base.BaseDb.Model(&system.SysDictType{}).Where("dict_type = ?", dictType).Take(&idModel)
	if result.Error != nil && errors.Is(result.Error, gorm.ErrRecordNotFound) {
		return true, nil
	} else if result.Error != nil {
		return false, result.Error
	}
	if result.RowsAffected == 0 || (idModel.ID == idReq.ID && idModel.ID != 0) {
		return true, nil
	}
	return false, nil
}

func (s *SysDictTypeService) DeleteDataTypeByIds(ids *[]uint64) (bool, error) {
	dictTypes, err := new(system.SysDictType).GetDictTypesByIds(nil, ids)
	if err != nil {
		return false, err
	}
	dictData, err := new(system.SysDictData).GetFirstByDictTypes(dictTypes)
	if err != nil {
		return false, err
	}
	if dictData != nil {
		return false, errors.New("存在字典数据，不允许删除")
	}
	dictType := system.SysDictType{}
	result := base.BaseDb.Model(&dictType).Where("id in (?)", *ids).Delete(&dictType)
	if result.Error == nil && result.RowsAffected > 0 {
		s.deleteDataTypeCacheByIds(*ids...)
	}
	return result.RowsAffected > 0, result.Error
}

/*
	   select dict_id, dict_name, dict_type, status, create_by, create_time, remark
			from sys_dict_type
*/
func (s *SysDictTypeService) GetDataTypeAll() ([]*system2.SysDictTypeItemResp, error) {
	itemsResp := make([]*system2.SysDictTypeItemResp, 0)
	err := s.loadAllDataTypeByCache(&itemsResp)
	if err != nil {
		return nil, err
	}
	if len(itemsResp) == 0 {
		err = s.loadAllDataTypeCache(&itemsResp)
		if err != nil {
			return nil, err
		}
	}
	return itemsResp, nil
}

func (s *SysDictTypeService) RefreshCache() error {
	if err := s.clearAllDataTypeCache(); err != nil {
		return err
	}
	itemsResp := make([]*system2.SysDictTypeItemResp, 0)
	return s.loadAllDataTypeCache(&itemsResp)
}

func (s *SysDictTypeService) clearAllDataTypeCache() error {
	strSliceCmd := framework.MoRedis.Keys(context.Background(), consts2.RedisKey_Dict_Type+"*")
	if strSliceCmd.Err() != nil {
		return strSliceCmd.Err()
	}
	return framework.MoRedis.Del(context.Background(), strSliceCmd.Val()...).Err()
}
func (s *SysDictTypeService) deleteDataTypeCacheByIds(ids ...uint64) {
	keys := make([]string, 0)
	for _, id := range ids {
		keys = append(keys, consts2.RedisKey_Dict_Type+strconv.FormatUint(id, 10))
	}
	framework.MoRedis.Del(context.Background(), keys...)
}

func (s *SysDictTypeService) getCacheDataTypeById(id uint64) (*system2.SysDictTypeItemResp, error) {
	strCmd := framework.MoRedis.Get(context.Background(), consts2.RedisKey_Dict_Type+strconv.FormatUint(id, 10))
	if strCmd.Err() != nil {
		return nil, strCmd.Err()
	}
	if strCmd.Val() == "" {
		return nil, nil
	}
	itemResp := new(system2.SysDictTypeItemResp)
	err := json.Unmarshal([]byte(strCmd.Val()), itemResp)
	return itemResp, err
}
func (s *SysDictTypeService) loadAllDataTypeByCache(itemsResp *[]*system2.SysDictTypeItemResp) error {
	for {
		scanCmd := framework.MoRedis.Scan(context.Background(), 0, consts2.RedisKey_Dict_Type+"*", 100)
		if scanCmd.Err() != nil {
			return scanCmd.Err()
		}
		// 获取扫描结果
		var keys []string
		keys, cursor, err := scanCmd.Result()
		if err != nil {
			return err
		}
		dictTypeItemsResp := *itemsResp
		// 处理扫描到的键
		for _, key := range keys {
			strCmd := framework.MoRedis.Get(context.Background(), key)
			if strCmd.Err() != nil {
				return strCmd.Err()
			}
			if strCmd.Val() == "" {
				continue
			}
			itemResp := new(system2.SysDictTypeItemResp)
			err := json.Unmarshal([]byte(strCmd.Val()), itemResp)
			if err != nil {
				return err
			}
			dictTypeItemsResp = append(dictTypeItemsResp, itemResp)
		}
		itemsResp = &dictTypeItemsResp
		// 如果游标为0，表示已经遍历完所有匹配的键
		if cursor == 0 {
			break
		}
	}
	return nil
}

func (s *SysDictTypeService) loadAllDataTypeCache(itemsResp *[]*system2.SysDictTypeItemResp) error {
	err := base.BaseDb.Model(&system.SysDictType{}).Find(&itemsResp).Error
	if err != nil {
		return err
	}
	for _, itemResp := range *itemsResp {
		dataBytes, err := json.Marshal(itemResp)
		if err != nil {
			return err
		}
		framework.MoRedis.Set(context.Background(), consts2.RedisKey_Dict_Type+strconv.FormatUint(itemResp.ID, 10), dataBytes, utils.RedisExpireDuration(consts2.Redis_Expire_Time))
	}
	return nil
}

func (s *SysDictTypeService) loadDataTypeToCacheById(id uint64) (*system2.SysDictTypeItemResp, error) {
	itemResp := system2.SysDictTypeItemResp{}
	err := base.BaseDb.Model(&system.SysDictType{}).Where("id = ?", id).First(&itemResp).Error
	if err != nil {
		return nil, err
	}
	err = s.setDataTypeToCache(id, &itemResp)
	if err != nil {
		return nil, err
	}
	return &itemResp, nil
}
func (s *SysDictTypeService) setDataTypeToCache(id uint64, itemResp *system2.SysDictTypeItemResp) error {
	buffers, err := json.Marshal(itemResp)
	if err != nil {
		return err
	}
	key := consts2.RedisKey_Dict_Type + strconv.FormatUint(id, 10)
	statusCmd := framework.MoRedis.Set(context.Background(), key, buffers, utils.RedisExpireDuration(consts2.Redis_Expire_Time))
	return statusCmd.Err()
}
func (s *SysDictTypeService) getDataTypeListVo(dictTypeReq *s1.SysDictTypePageReq) *gorm.DB {
	//selectFields := "id, dict_name, dict_type, status, create_by, created_at, remark"
	db := base.BaseDb.Model(&system.SysDictType{})
	if dictTypeReq.DictName != "" {
		db.Where("dict_name like ?", "%"+dictTypeReq.DictName+"%")
	}
	if dictTypeReq.Status != "" {
		db.Where("status = ?", dictTypeReq.Status)
	}
	if dictTypeReq.DictType != "" {
		db.Where("dict_type like ?", "%"+dictTypeReq.DictType+"%")
	}
	beginTime, ok := dictTypeReq.GetParamValue("beginTime")
	if ok && beginTime != "" {
		if beginTime = dictTypeReq.FormatBeginTime(beginTime); beginTime != "" {
			db.Where("created_at >= ?", beginTime)
		}
	}
	endTime, ok := dictTypeReq.GetParamValue("endTime")
	if ok && endTime != "" {
		if endTime = dictTypeReq.FormatEndTime(endTime); endTime != "" {
			db.Where("created_at <= ?", endTime)
		}
	}
	return db
}
